P1587 [NOI2016]循环之美

类比十进制下的纯循环小数,kk 进制下的纯循环小数满足最简分数分母与 kk 互质。

而题目要求相同数值只计数一次,所以只需要考虑最简分数的情况。

那么答案为:

i=1nj=1m[(i,j)=1][(j,k)=1]\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1][(j,k)=1]

i=1nj=1m[(i,j)=1]d(j,k)μ(d)\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1]\sum_{d|{(j,k)}}\mu(d)

dkμ(d)i=1nj=1md[(i,jd)=1]\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[(i,jd)=1]

dkμ(d)i=1nj=1md[(i,j)=1][(i,d)=1]\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[(i,j)=1][(i,d)=1]

dkμ(d)i=1mdj=1n[(i,j)=1][(j,d)=1]\sum_{d|k}\mu(d)\sum_{i=1}^{\lfloor \frac{m}{d} \rfloor}\sum_{j=1}^n[(i,j)=1][(j,d)=1]

后面的求和跟原来的形式相同 , 令 f(n,m,k)=i=1nj=1m[(i,j)=1][(j,k)=1]f(n,m,k)=\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1][(j,k)=1]

那么:

f(n,m,k)=dkf(md,n,d)f(n,m,k)=\sum_{d|k}f(\lfloor \frac{m}{d} \rfloor , n ,d)

n=0n=0m=0m=0f(n,m,k)=0f(n,m,k)=0

k=1k=1f(n,m,k)=i=1nj=1m[(i,j)=1]=d=1min(n,m)μ(d)ndmdf(n,m,k)=\sum_{i=1}^n\sum_{j=1}^m [(i,j)=1]=\sum_{d=1}^{\min(n,m)}\mu(d)\lfloor\frac{n}{d}\rfloor\rfloor\frac{m}{d}\lfloor

#include <map>
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long

const int MAXN = 1e6;
int n , m , k;

int num , prime[ MAXN + 5 ] , mu[ MAXN + 5 ] , summ[ MAXN + 5 ];
bool vis[ MAXN + 5 ];
void sieve( ) {
	mu[ 1 ] = 1;
	for( int i = 2 ; i <= MAXN ; i ++ ) {
		if( !vis[ i ] ) { prime[ ++ num ] = i; mu[ i ] = -1; }
		for( int j = 1 ; j <= num && 1ll * i * prime[ j ] <= MAXN ; j ++ ) {
			vis[ i * prime[ j ] ] = 1;
			if( i % prime[ j ] == 0 ) break;
			mu[ i * prime[ j ] ] = -mu[ i ];
		}
	}
	for( int i = 1 ; i <= MAXN ; i ++ ) summ[ i ] = summ[ i - 1 ] + mu[ i ];
}

map< int , int > Mapm;
int Summu( int n ) {
	if( n <= MAXN ) return summ[ n ];
	if( Mapm[ n ] ) return Mapm[ n ];
	int Ans = 1;
	for( int l = 2 , r ; l <= n ; l = r + 1 ) {
		r = n / ( n / l );
		Ans -= ( r - l + 1 ) * Summu( n / l ); 
	}
	return Mapm[ n ] = Ans;
}

LL f( int n , int m , int k ) {
	if( !n || !m ) return 0;
	if( k == 1 ) {
		long long Ans = 0;
		for( int l = 1 , r ; l <= min( n , m ) ; l = r + 1 ) {
			r = min( n / ( n / l ) , m / ( m / l ) );
			Ans += 1ll * ( Summu( r ) - Summu( l - 1 ) ) * ( n / l ) * ( m / l );
		}
		return Ans;
	}
	long long Ans = 0;
	for( int d = 1 ; d <= k ; d ++ )
		if( k % d == 0 && mu[ d ] ) Ans += mu[ d ] * f( m / d , n , d );
	return Ans;
}

int main( ) {
	sieve( );
	scanf("%d %d %d",&n,&m,&k);
	printf("%lld\n", f( n , m , k ) );
	return 0;
}